package com.haifeng.myhookdemo;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyHookUtil {

    private Context context;

    public void hookActivityHm(){
        try {
            Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThreradField=activityThreadClass.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThreradField.setAccessible(true);
            Object activityThreadObj=sCurrentActivityThreradField.get(null);
            Field mhfield=activityThreadClass.getDeclaredField("mH");
            mhfield.setAccessible(true);
            Handler mhHandler= (Handler) mhfield.get(activityThreadObj);
            Field mCallbackField=Handler.class.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);
            mCallbackField.set(mhHandler,new MyCallback(mhHandler));
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    class MyCallback implements Handler.Callback {
        private Handler systemHandler;

        public MyCallback(Handler systemHandler){
            this.systemHandler = systemHandler;
        }

        @Override
        public boolean handleMessage(Message msg) {
            if(msg.what == 100){
                //如果是 100 启动 Activity 的消息，我们需要特殊处理
                handleLaunchActivity(msg);
            }
            //这句话不能忘，其他的全部交给系统处理
            systemHandler.handleMessage(msg);
            return true;
        }

        /**
         * 如果执行到该方法，说明启动 Activity 已经经过 AMS 的检查了，
         * 我们在这里只需要将 Activity 进行还原即可。
         */
        private void handleLaunchActivity(Message msg) {
            /*
                这里有个小知识点，就是如果是启动 Activity 的话，肯定会有如下结构：
                msg 里面有一个 obj,而该 obj 中也会有一个 intent 属性
             */
            Object obj = msg.obj;
            try {
                Field intentField = obj.getClass().getDeclaredField("intent");
                intentField.setAccessible(true);

                //这个获得的是被我们篡改后的 Intent
                Intent tamperIntent = (Intent) intentField.get(obj);
                //从该 Intent 中获取真正想要跳转到的目标 Activity
                Intent oldIntent = tamperIntent.getParcelableExtra("oldIntent");
                if(oldIntent != null){
                    //实现集中式登录
                    SharedPreferences share = context.getSharedPreferences("radish",Context.MODE_PRIVATE);
                    boolean login = share.getBoolean("login", false);
                    if(login||oldIntent.getComponent().getClassName().equals(TwoActivity.class.getName())){
                        //登录了
                        tamperIntent.setComponent(oldIntent.getComponent());
                    }else{
                        //没登录
                        ComponentName componentName = new ComponentName(context,LoginActivity.class);
                        tamperIntent.putExtra("extraIntent",oldIntent.getComponent().getClassName());
                        tamperIntent.setComponent(componentName);
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public void hookAmsBefore26(Context context){
        this.context=context;
        try{
            Class<?>activityManagerClass  =Class.forName("android.app.ActivityManagerNative");


            Field field=activityManagerClass.getDeclaredField("gDefault");
            field.setAccessible(true);
            Object singletonObj=field.get(null);
            Class<?> singtonClass=Class.forName("android.util.Singleton");
            Field mInstanceField=singtonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);

            Object iActivityManagerObj=mInstanceField.get(singletonObj);

            Class<?> iActivityManagerIntercept=Class.forName("android.app.IActivityManager");
            StartActivityHandler startActivityHandler=new StartActivityHandler(iActivityManagerObj);
            Object proxyIActivityManager = Proxy.newProxyInstance(getClass().getClassLoader()
                    ,new Class[]{iActivityManagerIntercept},startActivityHandler);
            mInstanceField.set(singletonObj,proxyIActivityManager);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public  void hookAMSAfter26() throws Exception {
        // 第一步：获取 IActivityManagerSingleton
        Class<?> aClass = Class.forName("android.app.ActivityManager");
        Field declaredField = aClass.getDeclaredField("IActivityManagerSingleton");
        declaredField.setAccessible(true);
        Object value = declaredField.get(null);

        Class<?> singletonClz = Class.forName("android.util.Singleton");
        Field instanceField = singletonClz.getDeclaredField("mInstance");
        instanceField.setAccessible(true);
        Object iActivityManagerObject = instanceField.get(value);

        // 第二步：获取我们的代理对象，这里因为 IActivityManager 是接口，我们使用动态代理的方式
        Class<?> iActivity = Class.forName("android.app.IActivityManager");
        InvocationHandler handler = new StartActivityHandler(iActivityManagerObject);
        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new
                Class<?>[]{iActivity}, handler);

        // 第三步：偷梁换柱，将我们的 proxy 替换原来的对象
        instanceField.set(value, proxy);

    }

    public void hookAMSother() throws Exception{
        // 先获取到当前的ActivityThread对象
        Class<?> activityThreadClass  = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);
        //拿到在ActivityThread类里面的原始mInstrumentation对象
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);


        //构建我们的代理对象
        Instrumentation evilInstrumentation = new InstrumentationProxy(mInstrumentation);

        //通过反射，换掉字段，注意，这里是反射的代码，不是Instrumentation里面的方法
        mInstrumentationField.set(currentActivityThread, evilInstrumentation);

    }

    class InstrumentationProxy extends Instrumentation {


        public static final String TAG = "InstrumentationProxy";
        public static final String EXEC_START_ACTIVITY = "execStartActivity";

        // ActivityThread里面原始的Instrumentation对象,这里千万不能写成mInstrumentation,这样写
        //抛出异常，已亲测试，所以这个地方就要注意了
        public Instrumentation oldInstrumentationj;

        //通过构造函数来传递对象
        public InstrumentationProxy(Instrumentation mInstrumentations) {
            oldInstrumentationj = mInstrumentations;
        }


        //这个方法是由于原始方法里面的Instrumentation有execStartActivity方法来定的
        public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,
                                                Intent intent, int requestCode, Bundle options) {
            Log.d(TAG, "\n打印调用startActivity相关参数: \n" + "who = [" + who + "], " +
                    "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                    "\ntarget = [" + target + "], \nintent = [" + intent +
                    "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");


            Log.i(TAG, "------------hook  success------------->");
            Log.i(TAG, "这里可以做你在打开StartActivity方法之前的事情");
            Log.i(TAG, "------------hook  success------------->");
            Log.i(TAG, "");

            https://blog.51cto.com/u_12468355/3427579
            //由于这个方法是隐藏的，所以需要反射来调用，先找到这方法
            try {
                Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                        EXEC_START_ACTIVITY,
                        Context.class, IBinder.class, IBinder.class, Activity.class,
                        Intent.class, int.class, Bundle.class);
                execStartActivity.setAccessible(true);
                return (ActivityResult) execStartActivity.invoke(oldInstrumentationj, who,
                        contextThread, token, target, intent, requestCode, options);
            } catch (Exception e) {
                //如果你在这个类的成员变量Instrumentation的实例写错mInstrument,代码讲会执行到这里来
                throw new RuntimeException("if Instrumentation paramerter is mInstrumentation, hook will fail");
            }
        }
    }

    class StartActivityHandler implements InvocationHandler{
        //系统真正的对象
        private Object trueIActivityManager;

        public StartActivityHandler(Object trueIActivityManager) {
            this.trueIActivityManager = trueIActivityManager;
        }
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            if ("startActivity".equals(method.getName())){
                System.out.println("adb ：--------------------- startactivity--------------------");
                Intent intent=null;
                int index=-1;
                for (int i=0;i<objects.length;i++){
                    Object obj=objects[i];
                    if (obj instanceof Intent){
                        intent= (Intent) obj;
                        index=i;
                    }
                }
                Intent newIntent=new Intent(context,ProxyActivity.class);
                newIntent.putExtra("oldIntent",intent);
                objects[index]=newIntent;
            }
            return method.invoke(trueIActivityManager,objects);
        }
    }
}
